/*
* Copyright (c) 2010-2013 Ruben Chakhmakhchyan. All rights reserved.
* 
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Eclipse Public License
* Version 1.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://opensource.org/licenses/EPL-1.0 and read it before using this file.
*/

#include "stdafx.h"
#include "CScriptableControl.h"
#include <windowsx.h>
#include <ShlGuid.h>
#include <Dbt.h>

#define COMRELEASE( ptr ) if ( ptr != NULL ) { ptr->Release(); ptr = NULL; }

#include "plugin_impl.h"
#include "CScriptableDevice.h"
#include "CScriptableDevicecapability.h"
#include "CScriptableImageIndexer.h"
#include "CScriptableDeviceIndexer.h"

#include "strings.h"
#include "list.h"
#include "variant.h"
#include "convert.h"
#include "scripting.h"


CScriptableControl::CScriptableControl()
    : m_plugin( NULL ), m_model( NULL ),
    m_browser( NULL ),
    m_imageIndexer( NULL ),
    m_deviceIndexer( NULL ),
    m_userAgent( NULL )
{
    m_bWindowOnly = TRUE;
    //m_bWndLess = FALSE;

    m_plugin = new _Plugin( this );
    m_model = m_plugin->m_model;

    IUnknown* iu = NULL;
    if ( SUCCEEDED( ::CoCreateInstance( CLSID_ScriptableImageIndexer, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, ( void** ) &iu ) ) )
    {
        m_imageIndexer = ( CScriptableImageIndexer* ) iu;
        m_imageIndexer->setScriptableControl( this );

        m_dispImageIndexer = ( IDispatch* ) iu;
        m_dispImageIndexer->AddRef();
    }

    if ( SUCCEEDED( ::CoCreateInstance( CLSID_ScriptableDeviceIndexer, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, ( void** ) &iu ) ) )
    {
        m_deviceIndexer = ( CScriptableDeviceIndexer* ) iu;
        m_deviceIndexer->setScriptableControl( this );

        m_dispDeviceIndexer = ( IDispatch* ) iu;
        m_dispDeviceIndexer->AddRef();
    }
}

CScriptableControl::~CScriptableControl()
{
    COMRELEASE( m_imageIndexer );
    COMRELEASE( m_deviceIndexer );

    delete m_plugin;
    m_plugin = NULL;
}

//BOOL CIaControl::OnSetObjectRects( LPCRECT lpRectPos, LPCRECT lpRectClip )
//{
//	int i = 0;
//	return TRUE;
//}

LRESULT CScriptableControl::MessageHandler( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{
    bHandled = true;

    //_Plugin* p = ( _Plugin* ) m_pPlugin;

    switch ( uMsg )
    {
    case WM_CREATE:
        {
            RECT rect = { 0 };
            this->GetClientRect( &rect );
        }
        break;

    case WM_SETFOCUS:
        {
            int ii = 0;
        }
        break;

    case WM_SIZE:
        {
            unsigned int height = HIWORD( lParam );
            unsigned int width = LOWORD( lParam );

            m_model->resize( width, height );

            bHandled = true;

            return 0;
        }
        break;

    case WM_ERASEBKGND:
        {
            bHandled = m_model->hasImage();
        }
        break;

        /*case WM_PAINT:
        {
        RECT rc = { 0 };
        GetWindowRect( &rc );

        int width, height;
        m_pPlugin->m_model->getViewSize( width, height );

        int nwidth = rc.right - rc.left;
        int nheight = rc.bottom - rc.top;
        if ( width != nwidth || height != nheight )
        {
        m_pPlugin->m_model->setViewSize( nwidth, nheight );
        }

        m_pPlugin->m_model->render( di.hdcDraw );
        }
        break;*/

    case WM_KEYDOWN:
        {
            switch ( wParam )
            {
            case VK_SPACE:
                if ( m_model->keyDown( wParam ) )
                {
                    ::PostMessage( m_hWnd, WM_SETCURSOR, NULL, NULL );
                }
                break;
            }
        }
        break;

    case WM_KEYUP:
        {
            switch ( wParam )
            {
            case VK_SPACE:
                if ( m_model->keyUp( wParam ) )
                {
                    ::PostMessage( m_hWnd, WM_SETCURSOR, NULL, NULL );
                }
                break;
            }
        }
        break;

    case WM_LBUTTONDOWN:
        {
            short xpos = LOWORD( lParam );
            short ypos = HIWORD( lParam );

            m_model->mouseLeftDown( xpos, ypos );
        }
        break;

    case WM_LBUTTONUP:
        {
            short xpos = LOWORD( lParam );
            short ypos = HIWORD( lParam );

            m_model->mouseLeftUp( xpos, ypos );
        }
        break;

    case WM_MOUSEMOVE:
        {
            short xpos = LOWORD( lParam );
            short ypos = HIWORD( lParam );

            m_model->mouseMove( xpos, ypos );
        }
        break;

    case WM_DEVICECHANGE:
        {
            bool connected = wParam == DBT_DEVICEARRIVAL;
            bool disconnected = wParam == DBT_DEVICEREMOVECOMPLETE;

            if ( connected || disconnected )
            {
                ImageAcquisitionModel* ia = m_model->getIaModel();
                if ( ia != NULL )
                {
                    ia->deviceChange( connected );
                }
            }
        }
        break;

    case WM_CUSTOM_ASYNCCALLBACK:
        {
            // close thread handle
            //CloseHandle( g_pUploadThreadHandle );

            AsyncInvoke* asyncInvoke = ( AsyncInvoke* ) lParam;
            AsyncCallback asyncCallback = asyncInvoke->getCallback();
            asyncCallback( asyncInvoke->getArg() );

            // cleanup
            //UploadAsyncInvoke::Arg* arg = ( UploadAsyncInvoke::Arg* ) asyncInvoke->getArg();

            //delete arg;
            delete asyncInvoke;
        }
        break;

    default:
        bHandled = false;
    }

    return FALSE;
}

//STDMETHODIMP CIaControl::SetObjectRects(LPCRECT prcPos, LPCRECT prcClip)
//{
//	m_pPlugin->m_model->resize( prcClip->right - prcClip->left, prcClip->bottom - prcClip->top );
//
//	return S_OK;
//}

HRESULT CScriptableControl::OnDraw( ATL_DRAWINFO& di )
{
    RECT& rc = *( RECT* )di.prcBounds;
    // Set Clip region to the rectangle specified by di.prcBounds
    HRGN hRgnOld = NULL;
    if ( GetClipRgn( di.hdcDraw, hRgnOld ) != 1 )
        hRgnOld = NULL;
    bool bSelectOldRgn = false;

    int width, height;
    m_model->getViewSize( width, height );

    int nwidth = rc.right - rc.left;
    int nheight = rc.bottom - rc.top;
    if ( width != nwidth || height != nheight )
    {
        m_model->setViewSize( nwidth, nheight );
    }

    //HRGN hRgnNew = CreateRectRgn( rc.left, rc.top, rc.right, rc.bottom );

    //	if ( hRgnNew != NULL )
    {
        //	bSelectOldRgn = ( SelectClipRgn( di.hdcDraw, hRgnNew ) != ERROR );
    }

    //FrameRect( di.hdcDraw, &rc, GetStockBrush( LTGRAY_BRUSH ) );

    //SetTextAlign( di.hdcDraw, TA_CENTER | TA_BASELINE );

    //LPCTSTR pszText = _T( "ATL 8.0 : IaControl" );
    //TextOut( di.hdcDraw, ( rc.left + rc.right ) / 2, ( rc.top + rc.bottom ) / 2, pszText, lstrlen( pszText ) );

    m_model->render( di.hdcDraw );//, rc.right - rc.left, rc.bottom - rc.top );

    //if ( bSelectOldRgn )
    //	SelectClipRgn( di.hdcDraw, hRgnOld );

    return S_OK;
}

STDMETHODIMP CScriptableControl::SetClientSite( IOleClientSite *pClientSite ) 
{
    HRESULT hr = S_OK;

    hr = IOleObjectImpl<CScriptableControl>::SetClientSite( pClientSite );

    if ( !pClientSite ) return hr;

    IServiceProvider *isp, *isp2 = NULL;

    if ( !pClientSite )
    {
        COMRELEASE( m_browser );
    }
    else
    {
        hr = pClientSite->QueryInterface( IID_IServiceProvider, ( void** ) &isp );
        if ( FAILED( hr) )
        {
            hr = S_OK;
            goto cleanup;
        }

        hr = isp->QueryService( SID_STopLevelBrowser, IID_IServiceProvider, ( void** ) &isp2 );
        if ( FAILED( hr ) )
        {
            hr = S_OK;
            goto cleanup;
        }

        hr = isp2->QueryService( SID_SWebBrowserApp, IID_IWebBrowser2, ( void** ) &m_browser );
        if ( FAILED( hr ) )
        {
            hr = S_OK;
            goto cleanup;
        }

cleanup:
        // free resources

        COMRELEASE( isp );
        COMRELEASE( isp2 );
    }
    return hr;
}

void CScriptableControl::updateView()
{
    //SendMessage( m_hWnd, WM_PAINT, NULL, NULL );
    ::InvalidateRect( m_hWnd, NULL, true );
    ::UpdateWindow( m_hWnd );
}

IDispatch* CScriptableControl::listToArray( List<byte_t>& list )
{
    return ::listToArray( m_browser, list );
}

void scanCallback( AsyncInvokeArg* arg )
{
    arg->getPluginModel()->getPlugin()->onScanComplete( arg->getResult() );
}

STDMETHODIMP CScriptableControl::scan()
{
    //m_pPlugin->m_model->scan( true );

    m_model->beginScan( true, ::scanCallback );

    return S_OK;
}

STDMETHODIMP CScriptableControl::rescan()
{
    //m_pPlugin->m_model->scan( false );

    return S_OK;
}

void uploadCallback( AsyncInvokeArg* arg )
{
    arg->getPluginModel()->getPlugin()->onUploadComplete( arg->getId(), arg->getResultCode() );
}

STDMETHODIMP CScriptableControl::upload( VARIANT* pPages, VARIANT* pFilename, VARIANT* pFormat, VARIANT* pUrl, VARIANT* pUploadId, VARIANT* pResult )
{
    pResult->vt = VT_BOOL;
    pResult->boolVal = FALSE;

    if ( ( pFormat->vt != VT_BSTR ) || ( pUrl->vt != VT_BSTR ) )
    {
        return S_OK;
    }

    bool urlIsNull = pUrl->vt == VT_NULL;
    bool urlIsEmpty = pUrl->vt == VT_BSTR && ::SysStringLen( pUrl->bstrVal ) == 0;
    bool filenameIsNull = pFilename->vt == VT_NULL;
    bool filenameIsString = ::varIsString( pFilename );
    bool filenameIsEmpty = filenameIsString && ::SysStringLen( pFilename->bstrVal ) == 0;
    List<VARIANT> *filenameList = NULL;
    bool filenameIsArray = !filenameIsNull && !filenameIsString && ::variantToArray( pFilename, filenameList );
    bool scopeIsNull = pPages->vt == VT_NULL;
    int pageNumber = 0;
    bool scopeIsInt = ::varAsNumeric( pPages, pageNumber );
    bool scopeIsString = ::varIsString( pPages );
    List<VARIANT>* pageList = NULL;
    bool scopeIsArray = !scopeIsNull && !scopeIsInt && !scopeIsString && ::variantToArray( pPages, pageList );
    int uploadId = 0;

    if ( !::varAsNumeric( pUploadId, uploadId ) )
    {
        return S_OK;
    }

    if ( ( urlIsNull || ( pUrl->vt == VT_BSTR && String::IsUtf16Ascii( pUrl->bstrVal ) ) )
        && ( filenameIsNull || ( filenameIsString && String::IsUtf16Ascii( pFilename->bstrVal ) ) || filenameIsArray )
        && ( pFormat->vt == VT_BSTR && String::IsUtf16Ascii( pFormat->bstrVal ) )
        && ( scopeIsNull || scopeIsInt || ( scopeIsString && String::IsUtf16Ascii( pPages->bstrVal ) ) || scopeIsArray ) 
        )
    {
        // format
        DocumentFormat format = DocumentFormat::Default;
        bool formatValid = m_model->stringToDocumentFormat( pFormat->bstrVal, format );

#ifdef LITE
        if ( formatValid && format == DocumentFormatPdf )
        {
            m_pPlugin->onError( 1 ); // ERROR_FEATURENOTSUPPORTED

            return S_OK;
        }
#endif

        // url
        char* url = NULL;
        if ( !urlIsNull && !urlIsEmpty )
        {
            url = ::bstrToChars( pUrl->bstrVal );
        }

        List<char*>* filenameArray = NULL;
        //int filenameCount = 0;
        bool filenameValid = true;

        if ( filenameIsNull )
        {
        }
        else if ( filenameIsString && !filenameIsEmpty )
        {
            filenameArray->add( ::bstrToChars( pFilename->bstrVal ) );
            //	filenameCount = 1;
        }
        else if ( filenameIsArray )
        {
            int filenameCount = filenameList->count();
            filenameArray = new List<char*>( filenameCount );
            List<VARIANT> &filenameListRef = *filenameList;

            for ( int i = 0; i < filenameCount; i++ )
            {
                if ( ::varIsString( &( filenameListRef[ i ] ) ) )
                {
                    filenameArray->add( ::bstrToChars( filenameListRef[ i ].bstrVal ) );
                }
                else
                {
                    filenameValid = false;
                }
            }
        }

        int* indices = NULL;
        int indexCount = 0;
        bool pagesValid = true;

        if ( scopeIsString )
        {
            char* pagesString = ::bstrToChars( pPages->bstrVal );

            pagesValid = m_model->parseIndices( pagesString, indices, indexCount );

            delete pagesString;
        }
        else if ( scopeIsInt )
        {
            if ( pagesValid = pageNumber > 0 )
            {
                indices = new int[ 1 ];
                indices[ 0 ] = pageNumber;
                indexCount = 1;
            }
        }
        else if ( scopeIsArray )
        {
            indexCount = pageList->count();
            indices = new int[ indexCount ];
            List<VARIANT> &pageListRef = *pageList;

            for ( int i = 0; i < indexCount; i++ )
            {
                VARIANT* v = &( pageListRef[ i ] );
                int indexValue = 0;

                if ( ::varIsNumeric( v ) && ::varAsNumeric( v, indexValue ) )
                {
                    indices[ i ] = indexValue;
                }
                else
                {
                    pagesValid = false;
                }
            }
        }

        if ( formatValid && pagesValid && filenameValid )
        {
            bool uploadStarted = m_model->beginUpload( format, url, filenameArray, /*1,*/ indices, indexCount, ::uploadCallback, uploadId );

            pResult->boolVal = TRUE;
        }
        else
        {
            if ( filenameArray ) delete filenameArray; // TODO: not a complete cleanup
            if ( indices ) delete indices;
        }

        if ( pageList ) delete pageList;
        if ( filenameList ) delete filenameList;
    }
    return S_OK;
}

STDMETHODIMP CScriptableControl::saveToFile( VARIANT* pPages, VARIANT* pFormat, VARIANT* pPath )
{
    DocumentFormat documentFormat;

    if ( pPages->vt == VT_BSTR && pFormat->vt == VT_BSTR && pPath->vt == VT_BSTR )
    {
        if ( m_model->stringToDocumentFormat( pFormat->bstrVal, documentFormat ) )
        {
            m_model->saveToFile( pPath->bstrVal, documentFormat, NULL, 0 );
        }
    }

    return S_OK;
}

STDMETHODIMP CScriptableControl::getDevices()
{
    m_model->getDevices();

    return S_OK;
}

STDMETHODIMP CScriptableControl::get_devices( VARIANT* pResult )
{
    m_deviceIndexer->AddRef();

    pResult->vt = VT_DISPATCH;
    pResult->pdispVal = ( IDispatch* ) m_deviceIndexer;

    return S_OK;
}

STDMETHODIMP CScriptableControl::get_pixelType( VARIANT* pVal )
{
    pVal->vt = VT_INT;
    pVal->intVal = ( int ) m_model->getPixelType();

    return S_OK;
}

STDMETHODIMP CScriptableControl::put_pixelType( VARIANT* pVal )
{
    int value;
    if ( ::varAsNumeric( pVal, value ) )
    {
        m_model->setPixelType( ( PixelType ) value );
    }
    return S_OK;
}

STDMETHODIMP CScriptableControl::get_resolution( VARIANT* pVal )
{
    pVal->vt = VT_INT;
    pVal->intVal = ( int ) m_model->getResolution();

    return S_OK;
}

STDMETHODIMP CScriptableControl::put_resolution( VARIANT* pVal )
{
    int value;
    if ( ::varAsNumeric( pVal, value ) )
    {
        m_model->setResolution( value );
    }
    return S_OK;
}

STDMETHODIMP CScriptableControl::get_zoom( VARIANT* pVal )
{
    pVal->vt = VT_R8;
    pVal->dblVal = m_model->getZoom();

    return S_OK;
}

STDMETHODIMP CScriptableControl::put_zoom( VARIANT* pVal )
{
    if ( ::varIsNumeric( pVal ) )
    {
        m_model->setZoom( ::varAsNumeric<double>( pVal ) / 100.0F );
    }
    return S_OK;
}

STDMETHODIMP CScriptableControl::get_panTool( VARIANT* pVal )
{
    pVal->vt = VT_BOOL;
    pVal->boolVal = ( VARIANT_BOOL ) m_model->getPanState();

    return S_OK;
}

STDMETHODIMP CScriptableControl::put_panTool( VARIANT* pVal )
{
    if ( pVal->vt == VT_BOOL )
    {
        m_model->setPanState( !!pVal->boolVal );
    }
    return S_OK;
}

STDMETHODIMP CScriptableControl::get_cropTool( VARIANT* pVal )
{
    pVal->vt = VT_BOOL;
    pVal->boolVal = ( VARIANT_BOOL ) m_model->getCropState();

    return S_OK;
}

STDMETHODIMP CScriptableControl::put_cropTool( VARIANT* pVal )
{
    if ( pVal->vt == VT_BOOL )
    {
        m_model->setCropState( !!pVal->boolVal );
    }
    return S_OK;
}

STDMETHODIMP CScriptableControl::get_zoomTool( VARIANT* pVal )
{
    pVal->vt = VT_BOOL;
    pVal->boolVal = ( VARIANT_BOOL ) m_model->getZoomState();

    return S_OK;
}

STDMETHODIMP CScriptableControl::put_zoomTool( VARIANT* pVal )
{
    if ( pVal->vt == VT_BOOL )
    {
        m_model->setZoomState( !!pVal->boolVal );
    }
    return S_OK;
}

STDMETHODIMP CScriptableControl::get_driverModel( VARIANT* pVal )
{
    pVal->vt = VT_INT;
    pVal->intVal = m_model->getDriverModel();

    return S_OK;
}

STDMETHODIMP CScriptableControl::put_driverModel( VARIANT* pVal )
{
    if ( ::varIsNumeric( pVal ) )
        m_model->setDriverModel( ::varAsNumeric<int>( pVal ) );

    return S_OK;
}

STDMETHODIMP CScriptableControl::get_deviceIndex( VARIANT* pVal )
{
    pVal->vt = VT_INT;
    pVal->intVal = m_model->getDeviceIndex();

    return S_OK;
}

STDMETHODIMP CScriptableControl::put_deviceIndex( VARIANT* pVal )
{
    if ( ::varIsNumeric( pVal ) )
        m_model->setDeviceIndex( ::varAsNumeric<uint_t>( pVal ) );

    return S_OK;
}

STDMETHODIMP CScriptableControl::get_device( VARIANT* pVal )
{
    return get_deviceIndex( pVal );
}

STDMETHODIMP CScriptableControl::put_device( VARIANT* pVal )
{
    return put_deviceIndex( pVal );
}

STDMETHODIMP CScriptableControl::get_showDeviceUI( VARIANT* pVal )
{
    pVal->vt = VT_BOOL;
    pVal->boolVal = ( VARIANT_BOOL ) m_model->getShowDeviceUI();

    return S_OK;
}

STDMETHODIMP CScriptableControl::put_showDeviceUI( VARIANT* pVal )
{
    if ( pVal->vt == VT_BOOL )
    {
        m_model->setShowDeviceUI( !!pVal->boolVal );
    }
    return S_OK;
}

STDMETHODIMP CScriptableControl::get_selectedImageIndex( VARIANT* pVal )
{
    pVal->vt = VT_INT;
    pVal->intVal = ( int ) m_model->getSelectedImageIndex();

    return S_OK;
}

STDMETHODIMP CScriptableControl::put_selectedImageIndex( VARIANT* pVal )
{
    return S_OK;
}

STDMETHODIMP CScriptableControl::get_paperSource( VARIANT* pVal )
{
    pVal->vt = VT_INT;

    PaperSource paperSource = m_model->getPaperSource();
    int result = 0;
    if ( paperSource == PaperSource::Flatbed )
    {
        result = 1;
    }
    else if ( paperSource == PaperSource::Feeder )
    {
        result = 2;
    }
    pVal->intVal = result;

    return S_OK;
}

STDMETHODIMP CScriptableControl::put_paperSource( VARIANT* pVal )
{
    if ( ::varIsNumeric( pVal ) )
    {
        //if ( iaModelActive )
        {
            int value = ::varAsNumeric<int>( pVal );

            if ( value >= 0 && value <= 2 )
            {
                m_model->setPaperSource( ( PaperSource ) value );
            }
        }
        return true;
    }


    return S_OK;
}

STDMETHODIMP CScriptableControl::get_imageQuality( VARIANT* pVal )
{
    pVal->vt = VT_INT;
    pVal->intVal = m_model->getImageQuality();

    return S_OK;
}

STDMETHODIMP CScriptableControl::put_imageQuality( VARIANT* pVal )
{
    int i = 0;
    if ( ::varAsNumeric( pVal, i ) && ( i > 0 && i <= 100 ) )
    {
        m_model->setImageQuality( i );
    }
    return S_OK;
}

STDMETHODIMP CScriptableControl::get_autoPosition( VARIANT* pVal )
{
    pVal->vt = VT_BOOL;
    pVal->boolVal = m_model->getAutoPosition();

    return S_OK;
}

STDMETHODIMP CScriptableControl::put_autoPosition( VARIANT* pVal )
{
    if ( pVal->vt == VT_BOOL )
    {
        m_model->setAutoPosition( pVal->boolVal != 0 );
    }

    return S_OK;
}

STDMETHODIMP CScriptableControl::get_contrast( VARIANT* pVal )
{
    pVal->vt = VT_INT;
    pVal->intVal = m_model->getContrast();

    return S_OK;
}

STDMETHODIMP CScriptableControl::put_contrast( VARIANT* pVal )
{
    int i = 0;
    if ( ::varAsNumeric( pVal, i ) )
    {	
        m_model->setContrast( i );
    }
    return S_OK;
}

STDMETHODIMP CScriptableControl::get_version( VARIANT* pVal )
{
    pVal->vt = VT_BSTR;
    pVal->bstrVal = ::charToBstr( m_model->getVersion() );

    return S_OK;
}

STDMETHODIMP CScriptableControl::panX( VARIANT* pVal )
{
    int value;
    if ( ::varAsNumeric( pVal, value ) )
    {
        m_model->panX( value );
    }
    return S_OK;
}

STDMETHODIMP CScriptableControl::panY( VARIANT* pVal )
{
    int value;
    if ( ::varAsNumeric( pVal, value ) )
    {
        m_model->panY( value );
    }
    return S_OK;
}

STDMETHODIMP CScriptableControl::pan( VARIANT* pValX, VARIANT* pValY )
{
    int valueX, valueY;
    if ( ::varAsNumeric( pValX, valueX ) && ::varAsNumeric( pValY, valueY ) )
    {
        m_model->pan( valueX, valueY );
    }
    return S_OK;
}

STDMETHODIMP CScriptableControl::rotate( VARIANT* pVal )
{
    int value;
    if ( ::varAsNumeric( pVal, value ) )
    {
        m_model->rotate( value );
    }
    return S_OK;
}

STDMETHODIMP CScriptableControl::crop()
{
    m_model->crop();

    return S_OK;
}

STDMETHODIMP CScriptableControl::selectImage( VARIANT* pVal )
{
    int value = -1;
    if ( ::varAsNumeric<int>( pVal, value ) )
    {
        m_model->selectImage( value );
    }
    return S_OK;
}

STDMETHODIMP CScriptableControl::selectNextImage()
{
    m_model->selectNextImage();

    m_plugin->onSelectedImageChanged( 0 ); // implementation provides the index value

    return S_OK;
}

STDMETHODIMP CScriptableControl::selectPreviousImage()
{
    m_model->selectPreviousImage();

    m_plugin->onSelectedImageChanged( 0 ); // implementation provides the index value

    return S_OK;
}

STDMETHODIMP CScriptableControl::connect( VARIANT* pResult )
{
    pResult->vt = VT_BOOL;
    pResult->boolVal = m_model->connect();

    return S_OK;
}

STDMETHODIMP CScriptableControl::disconnect()
{
    m_model->disconnect();

    return S_OK;
}

STDMETHODIMP CScriptableControl::getCap( VARIANT* pCap, VARIANT* pResult )
{
    HRESULT hr = S_OK;

    if ( ::varIsNumeric( pCap ) && m_model->getIsConnected() )
    {
        uint_t index = ::varAsNumeric<uint_t>( pCap );

        BaseCap* baseCap = m_model->getIaModel()->getCap( index );

        if ( baseCap )
        {
            IUnknown* iu = NULL;
            IDispatch* idisp = NULL;

            if ( SUCCEEDED( ::CoCreateInstance( CLSID_ScriptableDeviceCapability, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, ( void** ) &iu ) ) )
            {
                CScriptableDeviceCapability* scriptableDeviceCapability = ( CScriptableDeviceCapability* ) iu;
                scriptableDeviceCapability->setData( baseCap );
                scriptableDeviceCapability->setScriptableControl( this );
            }

            iu->QueryInterface( IID_IDispatch, ( void** ) &idisp );

            pResult->vt = VT_DISPATCH;
            pResult->pdispVal = idisp;

            iu->Release();
        }
    }
    else
    {
        hr = S_FALSE;
    }

    return hr;
}

STDMETHODIMP CScriptableControl::clear()
{
    m_model->clearImages();

    return S_OK;
}

STDMETHODIMP CScriptableControl::setFilter( VARIANT* pFormat, VARIANT* pPixelType, VARIANT* pFilter )
{
    DocumentFormat documentFormat;
    int pixelType = -1;
    int filter = -1;

    if ( pFormat->vt == VT_BSTR && m_model->stringToDocumentFormat( pFormat->bstrVal, documentFormat ) 
        && ::varAsNumeric( pPixelType, pixelType ) && pixelType >= ( int ) PixelType::Bw && pixelType <= ( int ) PixelType::Rgb 
        && ::varAsNumeric( pFilter, filter ) && filter >= ( int ) ImageFilter::Flate && filter <= ( int ) ImageFilter::DCT )
    {
        m_model->setFilter( documentFormat, ( PixelType ) pixelType, ( ImageFilter ) filter );
    }

    return S_OK;
}

char* CScriptableControl::getUrl()
{
    BSTR bstr = NULL;

    HRESULT hr = m_browser->get_LocationURL( &bstr );

    char* url = ::bstrToChars( bstr );

    return url;
}

STDMETHODIMP CScriptableControl::get_autoCompress( VARIANT* pVal )
{
    pVal->vt = VT_BOOL;
    pVal->boolVal = m_model->getAutoCompress();

    return S_OK;
}

STDMETHODIMP CScriptableControl::put_autoCompress( VARIANT* pVal )
{
    if ( pVal->vt == VT_BOOL )
    {
        m_model->setAutoCompress( pVal->boolVal != 0 );
    }

    return S_OK;
}

STDMETHODIMP CScriptableControl::get_autoCompressionLevel( VARIANT* pVal )
{
    pVal->vt = VT_INT;
    pVal->intVal = m_model->getAutoCompressionLevel();

    return S_OK;
}

STDMETHODIMP CScriptableControl::put_autoCompressionLevel( VARIANT* pVal )
{
    int i = 0;
    if ( ::varAsNumeric( pVal, i ) && ( i > 0 && i <= 10 ) )
    {
        m_model->setAutoCompressionLevel( i );
    }

    return S_OK;
}

STDMETHODIMP CScriptableControl::get_autoCompressInBackground( VARIANT* pVal )
{
    pVal->vt = VT_BOOL;
    pVal->boolVal = m_model->getAutoCompressionLevel();

    return S_OK;
}

STDMETHODIMP CScriptableControl::put_autoCompressInBackground( VARIANT* pVal )
{
    if ( pVal->vt == VT_BOOL )
    {
        m_model->setAutoCompress( pVal->boolVal != 0 );
    }

    return S_OK;
}

STDMETHODIMP CScriptableControl::get_autoPause( VARIANT* pVal )
{
    pVal->vt = VT_BOOL;
    pVal->boolVal = m_model->getAutoPause();

    return S_OK;
}

STDMETHODIMP CScriptableControl::put_autoPause( VARIANT* pVal )
{
    if ( pVal->vt == VT_BOOL )
    {
        m_model->setAutoPause( pVal->boolVal != 0 );
    }

    return S_OK;
}

STDMETHODIMP CScriptableControl::resume( VARIANT* pProceed )
{
    if ( pProceed->vt == VT_BOOL )
    {
        m_model->resumeScan( pProceed->boolVal == 1 );
    }

    return S_OK;
}

STDMETHODIMP CScriptableControl::Load( LPPROPERTYBAG pPropBag, LPERRORLOG pErrorLog )
{
    /*if (pPropBag == NULL)
    {
    return E_POINTER;
    }

    HRESULT hrRetTemp;
    VARIANT varProperty;

    VariantInit(&varProperty);
    VariantClear(&varProperty);
    hrRetTemp = pPropBag->Read(L"windowed", &varProperty, pErrorLog);*/
    /*if (SUCCEEDED(hrRetTemp))
    {
    m_bWindowOnly = atol((LPCTSTR)_bstr_t(V_BSTR(&varProperty), true));
    }
    else
    {
    m_bWindowOnly = FALSE;
    }
    VariantClear(&varProperty);*/

    return IPersistPropertyBagImpl<CScriptableControl>::Load(pPropBag, pErrorLog);
}

STDMETHODIMP CScriptableControl::GetViewStatus(DWORD* pdwStatus)
{
    ATLTRACE(_T("IViewObjectExImpl::GetViewStatus\n"));
    *pdwStatus = VIEWSTATUS_SOLIDBKGND | VIEWSTATUS_OPAQUE;
    return S_OK;
}

STDMETHODIMP CScriptableControl::InterfaceSupportsErrorInfo(REFIID riid)
{
    static const IID* arr[] = { &IID_IScriptableControl, };
    for ( int i = 0; i < sizeof( arr ) / sizeof( arr[ 0 ] ); i++ )
    {
        if ( ::InlineIsEqualGUID( *arr[ i ], riid ) )
            return S_OK;
    }
    return S_FALSE;
}

STDMETHODIMP CScriptableControl::get_isPlatformSupported( VARIANT* pVal )
{
    pVal->vt = VT_BOOL;
    pVal->boolVal = m_model->getPlatformSupported();
    return S_OK;
}

STDMETHODIMP CScriptableControl::initializeDevice()
{
    m_model->initializeDevice();
    return S_OK;
}

STDMETHODIMP CScriptableControl::focus()
{
    setFocus();
    //SetControlFocus( TRUE );
    return S_OK;
}

STDMETHODIMP CScriptableControl::deleteImage( VARIANT* pIndex )
{
    int index = 0;
    if ( ::varIsNumeric( pIndex ) )
    {
        index = ::varAsNumeric<int>( pIndex );
    }
    else
    {
        index = m_model->getSelectedImageIndex();
    }
    m_model->deleteImage( index );

    return S_OK;
}

STDMETHODIMP CScriptableControl::shiftImage( VARIANT* pIndex )
{
    if ( ::varIsNumeric( pIndex ) )
    {
        int intValue = ::varAsNumeric<int>( pIndex );

        m_model->shiftImage( intValue );

        return true;
    }

    return S_OK;
}

STDMETHODIMP CScriptableControl::get_isFlatbedSupported( VARIANT* pResult )
{
    if ( m_model->getIaModel() && m_model->getDeviceIndex() > 0 )
    {
        pResult->vt = VT_BOOL;
        pResult->boolVal = m_model->getIaModel()->getIsDeviceFeatureSupported( DeviceFeature::Flatbed );
    }
    return S_OK;
}

STDMETHODIMP CScriptableControl::get_isFeederSupported( VARIANT* pResult )
{
    if ( m_model->getIaModel() && m_model->getDeviceIndex() > 0 )
    {
        pResult->vt = VT_BOOL;
        pResult->boolVal = m_model->getIaModel()->getIsDeviceFeatureSupported( DeviceFeature::Feeder );
    }

    return S_OK;
}

STDMETHODIMP CScriptableControl::get_isDuplexSupported( VARIANT* pResult )
{
    if ( m_model->getIaModel() && m_model->getDeviceIndex() > 0 )
    {
        pResult->vt = VT_BOOL;
        pResult->boolVal = m_model->getIaModel()->getIsDeviceFeatureSupported( DeviceFeature::Duplex );
    }
    return S_OK;
}

STDMETHODIMP CScriptableControl::get_selectedIndex( VARIANT* pResult )
{
    pResult->vt = VT_UINT;
    pResult->uintVal = m_model->getSelectedImageIndex();

    return S_OK;
}

STDMETHODIMP CScriptableControl::get_images( VARIANT* pResult )
{
    m_imageIndexer->AddRef();

    pResult->vt = VT_DISPATCH;
    pResult->pdispVal = ( IDispatch* ) m_imageIndexer;

    return S_OK;
}

void CScriptableControl::setFocus()
{
    ::SetFocus( m_hWnd );
}
